package com.itheima;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.*;

//自定义窗口类，创建对象，展示主窗口
public class MainFrame extends JFrame {

    //数字色块图片路径
    private static final String imagePath = "stone-maze/src/image/";

    //定义一个二维数组，用于存储数字色块图片的行列位置
    private int[][] imagePosition = {
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 10, 11, 12},
            {13, 14, 15, 0}
    };

    //定义一个二维数组，用于存储游戏通关时数字色块图片的行列位置
    private int[][] winImagePosition = {
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 10, 11, 12},
            {13, 14, 15, 0}
    };

    //定义两个int类型变量，用于记录当前空白色块的行索引和列索引
    private int row;
    private int col;

    //定义一个计数器，用于记录用户移动次数
    private int count = 0;

    //定义一个变量，用于记录历史胜利的最少步数
    private int minCount;

    public MainFrame() {

        //读取历史胜利的最少步数（放initImage()前面）
        minCount = readFileScore();
        System.out.println("历史胜利的最少步数：" + minCount);

        //1.调用一个初始化方法，初始化窗口的大小等基本信息
        initFrame();

        //4.打乱数字色块图片的顺序，再展示
        dislocateArray();

        //2.初始化游戏界面，展示数字色块图片
        initImage();

        //3.初始化窗口菜单：点击后弹出的信息为退出游戏和重新开始
        initMenu();

        //5.给当前窗口绑定上下左右按键事件
        bindKeyPressEvent();

        //设置窗口可见
        this.setVisible(true);

    }

    private void bindKeyPressEvent() {

        this.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {

                int keyCode = e.getKeyCode();       //获取键盘按键的编码

                switch (keyCode) {
                    case KeyEvent.VK_UP:        //用户按了上键，色块向上移动
                        move(Direction.UP);
                        break;
                    case KeyEvent.VK_DOWN:      //用户按了下键，色块向下移动
                        move(Direction.DOWN);
                        break;
                    case KeyEvent.VK_LEFT:      //用户按了左键，色块向左移动
                        move(Direction.LEFT);
                        break;
                    case KeyEvent.VK_RIGHT:     //用户按了右键，色块向右移动
                        move(Direction.RIGHT);
                        break;
                    default:
                        break;
                }

            }
        });

    }

    private void move(Direction direction) {
        //控制色块移动
        switch (direction) {
            case UP:
                //System.out.println("向上移动");

                //上移：空白色块和它下方色块交换位置
                //上移条件：当前空白色块的行索引 < imagePosition.length - 1
                if (row < imagePosition.length - 1) {
                    int temp = imagePosition[row][col];
                    imagePosition[row][col] = imagePosition[row + 1][col];
                    imagePosition[row + 1][col] = temp;
                    row++;      //更新当前空白色块的行索引
                    count++;        //更新移动次数
                }
                break;
            case DOWN:
                //System.out.println("向下移动");

                //下移：空白色块和它上方色块交换位置
                //下移条件：当前空白色块的行索引 > 0
                if (row > 0) {
                    int temp = imagePosition[row][col];
                    imagePosition[row][col] = imagePosition[row - 1][col];
                    imagePosition[row - 1][col] = temp;
                    row--;      //更新当前空白色块的行索引
                    count++;
                }
                break;
            case LEFT:
                //System.out.println("向左移动");

                //左移：空白色块和它右侧色块交换位置
                //左移条件：当前空白色块的列索引 < imagePosition[i].length - 1
                if (col < imagePosition[row].length - 1) {
                    int temp = imagePosition[row][col];
                    imagePosition[row][col] = imagePosition[row][col + 1];
                    imagePosition[row][col + 1] = temp;
                    col++;      //更新当前空白色块的列索引
                    count++;
                }
                break;
            case RIGHT:
                //System.out.println("向右移动");

                //右移：空白色块和它左侧色块交换位置
                //右移条件：当前空白色块的列索引 > 0
                if (col > 0) {
                    int temp = imagePosition[row][col];
                    imagePosition[row][col] = imagePosition[row][col - 1];
                    imagePosition[row][col - 1] = temp;
                    col--;      //更新当前空白色块的列索引
                    count++;
                }
                break;
        }

        //重新展示数字色块图片
        initImage();

    }

    //该打乱算法可能会产生无解的情况
    private void dislocateArray() {
        //打乱二维数组中元素顺序
        for (int i = 0; i < imagePosition.length; i++) {

            for (int j = 0; j < imagePosition[i].length; j++) {

                //在二维数组中随机获取一个行索引和列索引，再让该索引对应元素与当前元素交换
                int randomRow = (int) (Math.random() * imagePosition.length);
                int randomCol = (int) (Math.random() * imagePosition[i].length);

                int temp = imagePosition[i][j];
                imagePosition[i][j] = imagePosition[randomRow][randomCol];
                imagePosition[randomRow][randomCol] = temp;

            }
        }

        //找到空白色块
        OUT:
        for (int i = 0; i < imagePosition.length; i++) {

            for (int j = 0; j < imagePosition[i].length; j++) {

                if (imagePosition[i][j] == 0) {
                    row = i;
                    col = j;
                    break OUT;      //跳出循环

                }
            }
        }

    }

    private void initMenu() {

        JMenuBar menuBar = new JMenuBar();       //创建菜单栏
        JMenu menu = new JMenu("菜单");       //创建菜单

        JMenuItem exit = new JMenuItem("退出游戏");     //创建菜单项
        menu.add(exit);     //将菜单项添加到菜单中
        exit.addActionListener(e -> this.dispose());     //设置菜单项的点击事件

        JMenuItem restart = new JMenuItem("重新开始");
        menu.add(restart);
        restart.addActionListener(e -> {
            count = 0;
            dislocateArray();
            initImage();
        });

        menuBar.add(menu);              //将菜单添加到菜单栏中
        this.setJMenuBar(menuBar);      //将菜单栏添加到窗口中

    }

    private void initImage() {

        //先清空窗口上的全部原有图层，否则会重叠
        this.getContentPane().removeAll();      //清除内容面板上的组件(菜单通常位于JFrame的顶层，不在内容面板上，因此不会被清除)

        //给窗口添加一个展示移动次数的标签
        JLabel countTxt = new JLabel("当前移动：" + count + "次");
        countTxt.setBounds(10, 20, 150, 20);                 //设置标签位置
        countTxt.setForeground(Color.RED);                                     //设置字体颜色
        countTxt.setFont(new Font("宋体", Font.BOLD, 14));        //设置字体样式
        this.add(countTxt);

        //判断历史胜利的最少步数是否为0（第一次玩游戏），为0，则展示“无历史记录”
        if (minCount != 0) {
            //给窗口添加一个展示历史胜利的最少步数的标签
            JLabel scoreTxt = new JLabel("历史最小步数：" + minCount + "步");
            scoreTxt.setBounds(310, 20, 150, 20);
            scoreTxt.setForeground(Color.WHITE);
            scoreTxt.setFont(new Font("宋体", Font.BOLD, 14));
            this.add(scoreTxt);
        }else {
            //给窗口添加一个展示历史胜利的最少步数的标签
            JLabel scoreTxt = new JLabel("无历史最小步数");
            scoreTxt.setBounds(310, 20, 150, 20);
            scoreTxt.setForeground(Color.WHITE);
            scoreTxt.setFont(new Font("宋体", Font.BOLD, 14));
            this.add(scoreTxt);
        }

        //判断是否通关，如果通关，则展示胜利的图片
        //再与历史最少步数进行比较，如果当前步数小于历史最少步数，则更新历史最少步数
        if (isWin()) {

            //展示胜利的图片
            JLabel label = new JLabel(new ImageIcon(imagePath + "win.png"));
            label.setBounds(125, 230, 266, 88);
            this.add(label);

            //读取文件中的历史最少步数，看是否需要更新
            int fileMinCount = readFileScore();
            //文件中的历史最少步数为0或者当前胜利步数小于历史最少步数，则更新历史最少步数（为0说明是第一次玩游戏）
            if (fileMinCount == 0 || count < fileMinCount) {
                writeFileScore(count);
            }
        }

        //数字色块图片按4行4列的矩阵排布依次铺满整个窗口
        for (int i = 0; i < imagePosition.length; i++) {

            for (int j = 0; j < imagePosition[i].length; j++) {

                int number = imagePosition[i][j];           //获取数字色块图片的编号
                ImageIcon icon = new ImageIcon(imagePath + number + ".png");    //创建图片对象
                JLabel label = new JLabel(icon);        //创建标签对象
                label.setBounds(25 + j * 100, 60 + i * 100, 100, 100);     //设置标签位置
                this.add(label);        //将标签添加到窗口中

            }
        }

        //设置窗口的背景图片
        ImageIcon icon = new ImageIcon(imagePath + "background.png");
        JLabel label = new JLabel(icon);
        label.setBounds(0, 0, 450, 484);
        this.add(label);

        //刷新窗口的新图层
        this.repaint();

    }

    //把当前步数写入score.txt文件中
    private void writeFileScore(int count) {

        try (
                FileWriter fw = new FileWriter("stone-maze/src/score.txt");
                BufferedWriter bw = new BufferedWriter(fw);) {

            bw.write(count + "");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //读取score.txt文件中的历史胜利的最少步数
    private int readFileScore() {

        try (
                FileReader fr = new FileReader("stone-maze/src/score.txt");
                BufferedReader br = new BufferedReader(fr);) {

            String line = br.readLine();
            return Integer.valueOf(line);

        } catch (IOException e) {
            e.printStackTrace();
            return 0;
        }
    }

    private boolean isWin() {

        for (int i = 0; i < imagePosition.length; i++) {

            for (int j = 0; j < imagePosition[i].length; j++) {

                if (imagePosition[i][j] != winImagePosition[i][j]) {
                    return false;
                }
            }
        }

        return true;

    }

    private void initFrame() {

        this.setTitle("石子迷阵--v2.0--horse");                    //设置窗口标题
        this.setSize(465, 575);                    //设置窗口大小（根据背景图）
        this.setLocationRelativeTo(null);                        //设置窗口居中
        this.setLayout(null);                                   //设置窗口布局为绝对布局
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);    //设置窗口关闭方式

    }
}
